#include "Avalon/Threading/Timer.hpp"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#ifdef Yield
  #undef Yield
#endif
#include "Avalon/Base/Singleton.hpp"

using namespace Avalon;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::asio;
using namespace boost::posix_time;
using namespace boost::signals2;
using namespace std;

namespace {
  class TimerService : private Singleton<TimerService> {
    public:

      TimerService()
          : m_work(new io_service::work(m_service)),
            m_serviceThread(new thread(bind(&io_service::run,
              ref(m_service)))) {}

      virtual ~TimerService() {
        m_work.reset();
        m_service.stop();
        m_serviceThread->join();
      }

      boost::asio::io_service& GetService() {
        return m_service;
      }

      using Singleton<TimerService>::GetInstance;

    private:
      friend class Singleton<TimerService>;
      io_service m_service;
      scoped_ptr<io_service::work> m_work;
      scoped_ptr<thread> m_serviceThread;
  };
}

Timer::Timer()
    : m_repeat(false),
      m_running(false),
      m_timerService(NULL) {}

Timer::~Timer() {
  Stop();
}

time_duration Timer::GetInterval() {
  return m_interval;
}

void Timer::Start(time_duration interval, bool repeat,
    time_duration startTime) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(m_running) {
    return;
  }
  m_interval = interval + startTime;
  m_repeat = repeat;
  m_running = true;
  TimerService& service = TimerService::GetInstance();
  m_timerService = new deadline_timer(service.GetService());
  StartWait();
  m_interval = interval;
}

void Timer::Stop() {
  boost::unique_lock<boost::mutex> lock(m_mutex);
  if(!m_running) {
    return;
  }
  m_running = false;
  deadline_timer& r_timerService = *static_cast<deadline_timer*>(
    m_timerService);
  if(r_timerService.expires_from_now(time_duration()) == 0) {
    m_completeCondition.wait(lock);
  }
  delete static_cast<deadline_timer*>(m_timerService);
  m_timerService = NULL;
  m_interval = time_duration();
  m_repeat = false;
}

connection Timer::ConnectExpiredSignal(const ExpiredSignal::slot_type& slot) {
  return m_expiredSignal.connect(slot);
}

void Timer::StartWait() {
  deadline_timer& r_timerService = *static_cast<deadline_timer*>(
    m_timerService);
  r_timerService.expires_from_now(m_interval);
  r_timerService.async_wait(boost::bind(&Timer::HandleTrigger, this,
    boost::asio::placeholders::error));
}

void Timer::HandleTrigger(const boost::system::error_code& error) {
  if(error == asio::error::operation_aborted) {
    return;
  }
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(!m_running) {
    m_completeCondition.notify_all();
    return;
  }
  m_expiredSignal();
  if(m_repeat) {
    StartWait();
  } else {
    delete static_cast<deadline_timer*>(m_timerService);
    m_timerService = NULL;
    m_running = false;
  }
}
